package com.easoft.code.pos.ui.controllers;

import com.easoft.code.pos.ui.controllers.exception.ControllerException;
import com.easoft.code.pos.data.dao.GenericDao;
import com.easoft.code.pos.data.exception.CoreDataLayerException;
import com.easoft.code.pos.data.util.QueryParameter;
import com.easoft.code.pos.ui.beans.DataEntryField;
import com.easoft.code.pos.ui.beans.FormField;
import com.easoft.code.pos.ui.beans.FormFieldList;
import com.easoft.code.pos.ui.bind.UIBindManager;
import com.easoft.code.pos.ui.bind.UIBinder;
import com.easoft.code.pos.ui.models.PojoTableModel;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ComboBoxModel;
import javax.swing.JPanel;
import javax.swing.JTable;

/**
 *
 * @author Eliu
 * @param <T>
 */
public class DataEntryController<T> {

    private final GenericDao dao = GenericDao.getMe();
    private final Class<T> dataType;
    private final Map<String, ComboBoxModel> listModels;

    private List<String> tableColumnNames;
    private List<String> realTableColumnNames;
    private Integer[] colGridVisibles;
    private Integer[] colFormVisibles;
    private JPanel formContainer;
    private PojoTableModel tableModel;
    private JTable table;
    private T selRow;

    public DataEntryController(List<String> tableColumnNames, List<String> realTableColumnNames, Class<T> dataType,
            Integer[] colGridV, Integer[] colFormV) {
        this.listModels = new LinkedHashMap<>();
        this.tableColumnNames = tableColumnNames;
        this.realTableColumnNames = realTableColumnNames;
        this.dataType = dataType;
        this.colGridVisibles = colGridV;
        this.colFormVisibles = colFormV;
    }

    public DataEntryController(List<String> tableColumnNames, List<String> realTableColumnNames, Class<T> dataType) {
        this.listModels = new LinkedHashMap<>();
        this.tableColumnNames = tableColumnNames;
        this.realTableColumnNames = realTableColumnNames;
        this.dataType = dataType;
    }

    public void reload() {
        try {
            tableModel.setData(getData());
            tableModel.fireTableDataChanged();
        } catch (ControllerException ex) {
            Logger.getLogger(DataEntryController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void addNewModel(String k, ComboBoxModel v) {
        listModels.put(k, v);
    }

    public T getSelRow() {
        return selRow;
    }

    public void setSelRow(T selRow) {
        this.selRow = selRow;
    }

    public JPanel getFormContainer() {
        return formContainer;
    }

    public void setFormContainer(JPanel formContainer) {
        this.formContainer = formContainer;
    }

    public JTable getTable() {
        return table;
    }

    public void setTable(JTable table) {
        this.table = table;
    }

    public void doSave(T e) throws ControllerException {
        try {
            UIBindManager.masiveBindInput2Object(this.getClass().getSimpleName());
            dao.save(e);
        } catch (CoreDataLayerException ex) {
            throw new ControllerException("Error guardando: \n " + ex.getMessage(), ex);
        }
    }

    public void doUpdate(T e) throws ControllerException {
        try {
            UIBindManager.masiveBindInput2Object(this.getClass().getSimpleName());
            dao.update(e);
        } catch (CoreDataLayerException ex) {
            throw new ControllerException("Error actualizando: \n", ex);
        }
    }

    public void doDelete(T e) throws ControllerException {
        try {
            dao.remove(e);
        } catch (CoreDataLayerException ex) {
            throw new ControllerException("Error eliminado: \n", ex);
        }
    }

    public List<T> getData() throws ControllerException {
        try {
            List<T> t = (List<T>) dao.doNamedQuery(dataType.getSimpleName() + ".findAll", new QueryParameter[]{},
                    dataType, false);
            if (t != null && !t.isEmpty()) {
                return t;
            }
            return new LinkedList<>();
        } catch (CoreDataLayerException ex) {
            throw new ControllerException("Error guardando...", ex);
        }
    }

    private List<String> getVisibleColumns(Integer[] visibles, List<String> cols) {
        if (visibles == null) {
            return cols;
        }
        List<String> newList = new LinkedList<>();
        int c = 0;
        for (String col : cols) {
            if (Arrays.binarySearch(visibles, c) >= 0) {
                newList.add(col);
            }
            c++;
        }
        return newList;
    }

    public void configureUI(JPanel form, JTable grid) throws ControllerException {
        this.formContainer = form;
        this.table = grid;
        tableModel = new PojoTableModel(getData(), getVisibleColumns(colGridVisibles, tableColumnNames),
                getVisibleColumns(colGridVisibles, realTableColumnNames), dataType);

        table.setModel(tableModel);
    }

    public void selectionListener(Integer rowSelected) {
        if (rowSelected >= 0 && rowSelected < tableModel.getData().size()) {
            selRow = (T) tableModel.getData().get(rowSelected);
            generateFilledForm();
        }
    }

    /**
     *
     * @param selected
     * @param fd
     */
    private void bindField(T selected, DataEntryField fd) {
        try {
            String fName = "get" + fd.getSourceName();
            Method m = selected.getClass().getMethod(fName, new Class[]{});
            Object val = m.invoke(selected, new Object[]{});
            fd.setValue(val);
            fd.setTargetObject(selected);
            for (Method me : dataType.getMethods()) {
                if (me.getName().equals("set" + fd.getSourceName())) {
                    fd.setMethodListening(me);
                    break;
                }
            }
            fd.ready();
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException |
                NoSuchMethodException | SecurityException ex) {
            Logger.getLogger(DataEntryController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private boolean isFormColumnVisible(int i) {
        boolean show = false;
        if (this.colFormVisibles != null) {
            for (Integer j : this.colFormVisibles) {
                if (j == i) {
                    show = true;
                    break;
                }
            }
        } else { //sino hay configuracion presente
            return true;
        }

        return show;
    }

    public void generateFilledForm() {
        formContainer.removeAll();
        for (int i = 0; i < this.tableColumnNames.size(); i++) {
            boolean show = isFormColumnVisible(i);
            if (show) {
                String colName = this.tableColumnNames.get(i);
                DataEntryField fd = new FormField();
                if (listModels.containsKey(this.realTableColumnNames.get(i))) {
                    fd = new FormFieldList();
                    ((FormFieldList) fd).setModel((ComboBoxModel) listModels.get(this.realTableColumnNames.get(i)));
                }
                fd.setLabel(colName);
                fd.setSourceName(realTableColumnNames.get(i));
                bindField(selRow, fd);
                formContainer.add((JPanel) fd, formContainer.getComponentCount());
                formContainer.revalidate();
            }

        }
    }

    public void generateBlankForm() {
        try {
            selRow = dataType.newInstance();
            formContainer.removeAll();
            for (int i = 0; i < this.tableColumnNames.size(); i++) {
                boolean show = isFormColumnVisible(i);
                if (show) {
                    String colName = this.tableColumnNames.get(i);
                    FormField fd = new FormField();
                    fd.setLabel(colName);
                    fd.setSourceName(realTableColumnNames.get(i));
                    bindField(selRow, fd);
                    formContainer.add(fd, formContainer.getComponentCount());
                }
            }
            formContainer.revalidate();
        } catch (InstantiationException | IllegalAccessException ex) {
            Logger.getLogger(DataEntryController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void add2Binder(UIBinder binder) {
        UIBindManager.add(binder, this.getClass().getSimpleName());
    }

    public List<String> getTableColumnNames() {
        return tableColumnNames;
    }

    public void setTableColumnNames(List<String> tableColumnNames) {
        this.tableColumnNames = tableColumnNames;
    }

    public List<String> getRealTableColumnNames() {
        return realTableColumnNames;
    }

    public void setRealTableColumnNames(List<String> realTableColumnNames) {
        this.realTableColumnNames = realTableColumnNames;
    }
}
